package com.zackeus.sipservice;

import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;

import com.elvishew.xlog.XLog;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.trello.lifecycle4.android.lifecycle.AndroidLifecycle;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.zackeus.common.utils.ObjectUtils;
import com.zackeus.mvvm.base.BaseViewModel;
import com.zackeus.mvvm.event.RxBus;
import com.zackeus.sipservice.constant.Event;
import com.zackeus.sipservice.constant.SipStateCode;
import com.zackeus.sipservice.constant.SipStatusCode;
import com.zackeus.sipservice.entity.RtpStreamStats;
import com.zackeus.sipservice.entity.SipServiceEvent;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;

/**
 * SIP 事件接收器
 * @name: SipServiceEventReceiver
 * @author: zz
 * @date: 2022/9/20
 */
public class SipServiceEventReceiver {

    private static final String LOG_TAG = SipServiceEventReceiver.class.getSimpleName();
    private final Gson gson = new Gson();
    private final LifecycleProvider<Lifecycle.Event> lifecycle;
    private final CompositeDisposable mCompositeDisposable;

    public SipServiceEventReceiver(@NonNull BaseViewModel<?> viewModel) {
        this(viewModel.getLifecycleProvider());
    }

    public SipServiceEventReceiver(@NonNull LifecycleOwner owner) {
        this(AndroidLifecycle.createLifecycleProvider(owner));
    }

    public SipServiceEventReceiver(@NonNull LifecycleProvider<Lifecycle.Event> lifecycle) {
        this.lifecycle = lifecycle;
        this.mCompositeDisposable = new CompositeDisposable();
    }

    /**
     * 事件注册
     */
    public void register() {
        synchronized (this) {
            if (this.mCompositeDisposable.size() > 0)
                return;

            this.mCompositeDisposable.add(RxBus.getInstance().toObservable(SipServiceEvent.class)
                    .compose(this.lifecycle.bindToLifecycle())
                    /* 回调到主线程 */
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(sipServiceEvent -> {
                        JsonObject msg = sipServiceEvent.getMsg();
                        if (ObjectUtils.isNull(msg)) {
                            XLog.tag(LOG_TAG).w("The Sip msg is empty, so will be ignored.");
                            return;
                        }

                        switch (sipServiceEvent.getTag()) {
                            case Event.EVENT_CALL_RECONNECTION_STATE:
                                this.onCallReconnectionState(msg.get("callId").getAsInt(), msg.get("state").getAsBoolean());
                                break;

                            case Event.EVENT_REGISTRATION_STARTED:
                                this.onRegistrationStarted(msg.get("accountId").getAsString(), msg.get("renew").getAsBoolean());
                                break;

                            case Event.EVENT_REGISTRATION_STATE:
                                this.onRegistrationState(
                                        msg.get("accountId").getAsString(),
                                        msg.get("status").getAsInt(),
                                        msg.get("statusCode").getAsInt(),
                                        msg.get("reason").getAsString()
                                );
                                break;

                            case Event.EVENT_CALL_STATE:
                                this.onCallState(
                                        msg.get("accountId").getAsString(),
                                        msg.get("callId").getAsInt(),
                                        msg.get("callRole").getAsInt(),
                                        msg.get("callState").getAsInt(),
                                        msg.get("callStatus").getAsInt(),
                                        msg.get("confirmedTimestamp").getAsLong()
                                );
                                break;

                            case Event.EVENT_CALL_STATS:
                                this.onCallStats(
                                        msg.get("accountId").getAsString(),
                                        msg.get("callId").getAsInt(),
                                        msg.get("connectTime").getAsInt(),
                                        msg.get("totalTime").getAsInt(),
                                        msg.get("audioCodec").getAsString(),
                                        msg.get("hangupStatus").getAsInt(),
                                        gson.fromJson(msg.get("rxStat").getAsString(), RtpStreamStats.class),
                                        gson.fromJson(msg.get("txStat").getAsString(), RtpStreamStats.class)
                                );
                                break;

                            case Event.EVENT_OUTGOING_CALL:
                                this.onOutgoingCall(
                                        msg.get("accountId").getAsString(),
                                        msg.get("callId").getAsInt(),
                                        msg.get("userName").getAsString(),
                                        msg.get("displayName").getAsString()
                                );
                                break;

                            case Event.EVENT_INCOMING_CALL:
                                this.onIncomingCall(
                                        msg.get("accountId").getAsString(),
                                        msg.get("callId").getAsInt(),
                                        msg.get("userName").getAsString(),
                                        msg.get("displayName").getAsString()
                                );
                                break;

                            default:
                                break;
                        }

                    }));
        }
    }

    /**
     * 注销事件
     */
    public void unRegister() {
        if (mCompositeDisposable != null) {
            mCompositeDisposable.clear();
        }
    }

    /**
     * 呼叫重新连接
     * @param state
     */
    @CallSuper
    protected void onCallReconnectionState(int callId, boolean state) {
        XLog.tag(LOG_TAG).d("Call reconnection state " + state);
    }

    /**
     * 注册/注销
     * @param accountId 账号ID
     * @param renew
     *  true: 注册
     *  false: 注销
     */
    @CallSuper
    protected void onRegistrationStarted(String accountId, boolean renew) {
        XLog.tag(LOG_TAG).d("onRegistrationStarted - accountId: " + accountId + ", renew: " + renew);
    }

    /**
     * 注册状态
     * @param accountId 账号ID
     * @param status 注册业务状态
     * @param statusCode SIP状态码
     * @param reason SIP原因短语
     */
    @CallSuper
    protected void onRegistrationState(String accountId, int status, @SipStatusCode.Unit int statusCode, String reason) {
        XLog.tag(LOG_TAG).d("onRegistrationState - accountId: " + accountId + ", statusCode: " + statusCode);
    }

    /**
     * 会话状态
     * @param accountId 账号ID
     * @param callId 会话ID
     * @param callRole 会话方向
     * @param callState 呼叫状态
     * @param callStatus 呼叫状态码
     * @param confirmedTimestamp 通话建立时间戳 (未建立时为0)
     */
    @CallSuper
    protected void onCallState(String accountId, int callId, int callRole,
                               @SipStateCode.Unit int callState, @SipStatusCode.Unit int callStatus, long confirmedTimestamp) {
        XLog.tag(LOG_TAG).d("onCallState - accountId: " + accountId
                + ", callId: " + callId + ", callRole: " + callRole + ", callState: " + callState);
    }

    /**
     * 会话统计
     * @param accountId 账号ID
     * @param callId 会话ID
     * @param connectTime 呼叫连接持续时长 (呼叫未建立时为零)
     * @param totalTime 总时长 包括设置时间
     * @param audioCodec 音频编码
     * @param hangupStatus 挂断状态码
     * @param rx 解码器流统计
     * @param tx 编码器流统计
     */
    @CallSuper
    protected void onCallStats(String accountId, int callId, int connectTime , int totalTime,
                             String audioCodec, @SipStatusCode.Unit int hangupStatus,
                             RtpStreamStats rx, RtpStreamStats tx) {
        XLog.tag(LOG_TAG).d("onCallState - accountId: " + accountId + ", callId: " + callId
                + ", connectTime: " + connectTime + ", totalTime: " + totalTime + ", hangupStatus: " + hangupStatus);
    }

    /**
     * 外呼
     * @param accountId 账号ID
     * @param callId 会话ID
     * @param userName sip用户名
     * @param displayName 显示名称
     */
    @CallSuper
    protected void onOutgoingCall(String accountId, int callId, String userName, String displayName) {
        XLog.tag(LOG_TAG).d("onOutgoingCall - accountId: " + accountId + ", callId: " + callId + ", userName: " + userName);
    }

    /**
     * 来电
     * @param accountId 账号ID
     * @param callId 会话ID
     * @param userName sip用户名
     * @param displayName 显示名称
     */
    @CallSuper
    protected void onIncomingCall(String accountId, int callId, String userName, String displayName) {
        XLog.tag(LOG_TAG).d("onIncomingCall - accountId: " + accountId + ", callId: " + callId + ", userName: " + userName);
    }
}
